﻿#pragma once

#include <functional>
#include <map>
#include <string>
#include <iostream>
#include <vector>

#include <boost/any.hpp>
#include <boost/lexical_cast.hpp>

// 简单的消息分发机制
// ref:https://www.cnblogs.com/qicosmos/archive/2013/04/28/3048919.html
// 《深入应用C++11代码优化与工程级应用》第12章
// tuple: http://www.cplusplus.com/reference/tuple/tuple/?kw=tuple
class RegCenter
{
public:
    // regist message
    template< class... _Args, class _Func, class = typename std::enable_if<!std::is_member_function_pointer<_Func>::value>::type>
    void attach(std::string key, _Func && func)
    {
        std::function<void(_Args...)> fn = [&](_Args... args) { return func(std::forward<_Args>(args)...); };
        m_map.insert(std::make_pair(key, std::make_tuple(std::move(fn), sizeof ...(_Args))));
    }

    // non-const member function
    template<class... _Args, class _Class, class... _DeclareArgs, class _Object>
    void attach(std::string key, void(_Class::*func)(_DeclareArgs...), _Object & object)
    {
        std::function<void(_Args...)> fn = [&, func](_Args... args) { return (object.*func)(std::forward<_Args>(args)...); };
        m_map.insert(std::make_pair(key, std::make_tuple(fn, sizeof ...(_Args))));
    }

    // const member function
    template<class... _Args, class _Class, class... _DeclareArgs, class _Object>
    void attach(std::string key, void(_Class::*func)(_DeclareArgs...) const, _Object & object)
    {
        std::function<void(_Args...)> fn = [&, func](_Args... args) { return (object.*func)(std::forward<_Args>(args)...); };
        m_map.insert(std::make_pair(key, std::make_tuple(fn, sizeof ...(_Args))));
    }

    // Broadcast messages, call all the callback functions
    template<typename... _Args>
    int sendMessage(std::string key, _Args... args)
    {
        auto range = m_map.equal_range(key);
        for (auto it = range.first; it != range.second; it++)
        {
            if (std::get<1>(it->second) != sizeof ...(args)) // number of args must be matched
                return -2;
            std::function<void(_Args...)> func = boost::any_cast<std::function<void(_Args...)>>(std::get<0>(it->second));
            func(args...);
            return 0;
        }
        return -1;
    }

    // remove message
    template<typename... _Args>
    void remove(std::string key)
    {
        auto range = m_map.equal_range(key);
        m_map.erase(range.first, range.second);
    }

public:
    static RegCenter& instance()
    {
        static RegCenter ins;
        return ins;
    }

    std::vector<std::string> showall()
    {
        std::vector<std::string> all;
        for (auto& cmd : m_map)
        {
            all.push_back(cmd.first);
        }
        return all;
    }

private:
    RegCenter(const RegCenter&) = delete;
    RegCenter& operator=(const RegCenter&) = delete;
    RegCenter() = default;
    ~RegCenter() = default;

    std::multimap<std::string, std::tuple<boost::any, uint8_t>> m_map; // std::multimap<key, std::tuple<callbackFunc, argsNum>>
};
